import sys
import json
from PySide6 import QtGui, QtCore, QtWidgets
from PySide6.QtCore import Qt, QJsonValue, QPoint
from PySide6.QtGui import QStandardItemModel, QStandardItem, QCursor, QColor
from PySide6.QtWidgets import QApplication, QTreeView, QAbstractItemView, QMenu, QMessageBox,QDialog,QVBoxLayout


class JsonEditDialog(QDialog):

    def __init__(self, parent=None, json_str=None, signal=None, Ro_flag=False):
        super().__init__(parent)
        self.signal = signal
        self.Ro_flag = Ro_flag

        self.jsonModel = JsonModel(json_str)
        self.treeView = QTreeView(self)
        layout = QVBoxLayout(self)
        layout.addWidget(self.treeView)
        self.treeView.setModel(self.jsonModel)
        self.treeView.expandAll()
        self.treeView.setSelectionMode(
            QAbstractItemView.SelectionMode.NoSelection)

        style_sheet = "QTreeView::item { border: 2px solid gray; }"
        self.treeView.setStyleSheet(style_sheet)

        if self.Ro_flag:
            self.treeView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        else:
            self.treeView.setContextMenuPolicy(
                Qt.ContextMenuPolicy.CustomContextMenu)
            self.treeView.customContextMenuRequested.connect(self.rightClick_menu)

    def closeEvent(self, event) -> None:
        result = self.jsonModel.getJsonData()
        if type(result) == dict:
            result = dict(sorted(result.items(), key=lambda item: int(item[0])))
        else:
            pass
        # 将dict或list转为json格式返回，ensure_ascii是避免中文编码问题
        json_str = json.dumps(result, ensure_ascii=False)
        # 判断返回的数据中是否有因未保存导致的NULL值，有的话需要提醒修改
        if json_str != 'null' and ('null' in json_str or 'None' in json_str):
            QMessageBox.information(self, '提示', '输入的结果中有空值NULL\n或是有还未保存的值\n请重试', QMessageBox.Yes)
            event.ignore()
            return
        self.signal.emit(json_str)
        return super().closeEvent(event)

    def rightClick_menu(self, event):
        """右键菜单"""
        index = self.treeView.indexAt(QPoint(event.x(), event.y()))
        if index.column() == 0 or index.row() == -1:
            self.treeView.contextMenu = QMenu()
            self.treeView.contextMenu.addAction('add').triggered.connect(
                lambda: self.jsonModel.addItem(index))
            if index.row() != -1:
                self.treeView.contextMenu.addAction(
                    'delete').triggered.connect(
                        lambda: self.jsonModel.deleteNode(index))
            self.treeView.contextMenu.popup(QCursor.pos())
            self.treeView.contextMenu.show()


class JsonModel(QStandardItemModel):
    """
        用tree的方式通过key-value的方式显示一个json
        当value是list时,        key显示为 self.delimiter
        当value是list或dict时   value显示为 self.delimiter
    """

    def __init__(self, json_str):
        super().__init__()
        self.setHorizontalHeaderLabels(('key', 'value'))
        self.delimiter = '--'
        self.updateJson(json_str)

    def updateJson(self, json_str):
        if json_str == '':
            json_data = {}
        else:
            json_data = json.loads(json_str)
        self.loadJson(json_data)

    def loadJson(self, json_data, parent=None):
        """
        递归遍历 JSON字符串 创建QStandardItemModel
        """
        if not parent:
            parent = self.invisibleRootItem()

        if isinstance(json_data, dict):
            for key in json_data.keys():
                key_item = QStandardItem(key)
                if not isinstance(json_data[key], dict) and not isinstance(
                        json_data[key], list):
                    value_item = QStandardItem(str(json_data[key]))
                else:
                    value_item = self.delimiterItem()
                parent.appendRow([key_item, value_item])
                self.loadJson(json_data[key], key_item)

        elif isinstance(json_data, list):

            for value in json_data:
                key_item = self.delimiterItem()
                if not isinstance(value, dict) and not isinstance(value, list):
                    value_item = QStandardItem(str(value))
                else:
                    value_item = self.delimiterItem()
                parent.appendRow([key_item, value_item])
                self.loadJson(value, key_item)

    def getJsonData(self, parent=None):
        """
        递归遍历 QStandardItemModel 中的所有节点，返回 JSON 数据
        """

        if not parent:
            parent = self.invisibleRootItem()

        if parent.hasChildren():
            # 判断当前节点是list 还是 dict
            if parent.child(0, 0).text() == self.delimiter:
                json_data = []
            else:
                json_data = {}

            for row in range(parent.rowCount()):
                key_item = parent.child(row, 0)
                value_item = parent.child(row, 1)
                if value_item.text() != self.delimiter:
                    child = value_item
                else:
                    child = key_item
                if key_item.text() == self.delimiter:
                    json_data.append(self.getJsonData(parent=child))
                else:
                    json_data[key_item.text()] = self.getJsonData(parent=child)
        else:
            json_data = parent.text()
            if json_data == '':
                json_data = None

        return json_data

    def delimiterItem(self, item=None):
        """
            设置占位
        """
        if item is None:
            item = QStandardItem(self.delimiter)
        else:
            item.setText(self.delimiter)
        item.setBackground(QtGui.QBrush(QtGui.QColor(204, 204, 204)))
        item.setEnabled(False)
        return item

    def addItem(self, index):
        """
            添加JSON
            自动判断是否只能添加list或dict
            否则询问添加list or dict

        """
        parent = self.itemFromIndex(index)
        if parent is None:
            parent = self.invisibleRootItem()
        if parent.hasChildren():
            if parent.child(0, 0).text() == self.delimiter:
                itemList = [self.delimiterItem(), QStandardItem()]  # 只能添加list
            else:
                itemList = [QStandardItem(), QStandardItem()]  # 只能添加dict
        else:
            # 获取value_item
            value_index = index.sibling(index.row(), index.column() + 1)
            value_item = self.itemFromIndex(value_index)
            if value_item is not None:
                # key 和 value 都不是 delimiter 时说明不能添加
                if value_item.text() != self.delimiter and parent.text(
                ) != self.delimiter:
                    return

            # 询问添加dict or list
            msg_box = QMessageBox()
            msg_box.setText("Add list or dict?")
            msg_box.setStandardButtons(QMessageBox.StandardButton.Yes
                                       | QMessageBox.StandardButton.Ok
                                       | QMessageBox.StandardButton.Cancel)
            btn1 = msg_box.button(QMessageBox.StandardButton.Yes)
            btn1.setText('List')
            btn2 = msg_box.button(QMessageBox.StandardButton.Ok)
            btn2.setText('Dict')
            reply = msg_box.exec()

            if reply == QMessageBox.StandardButton.Yes:
                itemList = [self.delimiterItem(), QStandardItem()]
            elif reply == QMessageBox.StandardButton.Ok:
                itemList = [QStandardItem(), QStandardItem()]
            else:
                return
            # 将当前的value_item更改为占位符
            self.delimiterItem(value_item)

        parent.appendRow(itemList)

    def deleteNode(self, index):
        self.removeRow(index.row(), index.parent())





if __name__ == "__main__":
    app = QApplication(sys.argv)

    json_str = '''
    {

        "pets": [
            {
                "name": "Rufus",
                "type": "dog"
            },
            {
                "name": "Mittens",
                "type": "cat"
            }
        ],
        "city": ["New York","Beijing"],
        "name": "John",
        "age": 30
    }
    '''
    jsonEditDialog = JsonEditDialog(json_str=json_str)
    jsonEditDialog.show()

    sys.exit(app.exec())
